#include "../include/CRenderPlayer.h"
#include "../include/CPlayerBehaviour.h"
#include "../include/CDefaultShotBehaviour.h"
#include "../include/CRenderEnemy.h"
#include "../include/CDefaultEnemyBehaviour.h"
#include "../include/CDefaultPowerUpBehaviour.h"
#include "../include/CSprite.h"

#include <SDL/SDL.h>

//**************************
//Description : Constructor
//Parameters : The screen
//Note : None
//**************************
CRenderPlayer::CRenderPlayer()
    : IRenderEntity()
{
    //the player blink at first
    m_iState = BLINK;
    m_lBeginBlink = SDL_GetTicks();

    m_PlayerEntity = NULL;
}

//**************************
//Description : Constructor
//Parameters : The screen,
//             A pointer toward the player entity
//             Animations for player and its shots
//Note : None
//**************************
CRenderPlayer::CRenderPlayer(SDL_Surface * _screen,
                        CPlayerBehaviour * _Player,
                        CSpriteAnimation * _PlayerAnimations,
                        CSpriteAnimation * _PlayerShotsAnimation)
    : IRenderEntity(_screen)
{
    //the player blink at first
    m_iState = BLINK;
    m_lBeginBlink = SDL_GetTicks();

    SetSprites(_PlayerAnimations, _PlayerShotsAnimation);

    SetPlayerBehaviour(_Player);
}

//**************************
//Description : Copy Constructor
//Parameters : A CRenderPlayer
//Note : None
//**************************
CRenderPlayer::CRenderPlayer(CRenderPlayer & _RenderPlayer)
    : IRenderEntity(_RenderPlayer),
    m_iInitX(_RenderPlayer.m_iInitX),
    m_iInitY(_RenderPlayer.m_iInitY),
    m_lBeginBlink(_RenderPlayer.m_lBeginBlink)
{
    //the player blink at first
    m_iState = BLINK;
    m_lBeginBlink = SDL_GetTicks();

    SetPlayerBehaviour( _RenderPlayer.GetPlayerBehaviour() );
}

//**************************
//Description : Destructor
//Parameters : None
//Note : None
//**************************
CRenderPlayer::~CRenderPlayer()
{
    if ( m_PlayerEntity != NULL )
        delete m_PlayerEntity;
}

//**************************
//Description : Set the initial value for the rendering
//Parameters : The screen,
//             A pointer toward the player entity
//             Animations for player and its shots
//Return Value : None
//Note : None
//**************************
void CRenderPlayer::SetRender(SDL_Surface * _screen,
                        CPlayerBehaviour * _Player,
                        CSpriteAnimation * _PlayerAnimations,
                        CSpriteAnimation * _PlayerShotsAnimation)
{
    m_Screen = _screen;

    SetSprites(_PlayerAnimations, _PlayerShotsAnimation);

    SetPlayerBehaviour(_Player);
}

//**************************
//Description : Initialise the rendering to its beginning
//Parameters : None
//Return Value : None
//Note : None
//**************************
void CRenderPlayer::InitRender()
{
    m_PlayerEntity->ClearShots();
    m_PlayerEntity->SetPosition(m_iInitX, m_iInitY);

    for (int i = 0; i < NUMBER_STATES; i++)
        m_vEntitySprites[i].RewindAnim();

    m_iState = BLINK;
    m_lBeginBlink = SDL_GetTicks();

    //we don't restore the life of the player, why ? because an init render is called when we restart a level
    //after we die, so we don't restore life after we die...
}

//**************************
//Description : Set sprites for the rendering if it's not done
//Parameters : Animations for the player and its shots
//Return Value : None
//Note : None
//**************************
void CRenderPlayer::SetSprites(CSpriteAnimation * _PlayerAnimations,
                        CSpriteAnimation * _PlayerShotsAnimation)
{
    IRenderEntity::SetSprites(_PlayerAnimations);

    //we set the animations of the shots
    m_vShotsAnimations = _PlayerShotsAnimation;
}

//**************************
//Description : Return the Player Entity
//Parameters : None
//Return Value : The player entity
//Note : None
//**************************
CPlayerBehaviour * CRenderPlayer::GetPlayerBehaviour() const
{
    return m_PlayerEntity;
}

//**************************
//Description : Set m_PlayerEntity if it's not done
//Parameters : A pointer toward a player
//Return Value : None
//Note : None
//**************************
void CRenderPlayer::SetPlayerBehaviour(CPlayerBehaviour * _Player)
{
    if ( m_PlayerEntity == NULL )
    {
        m_PlayerEntity = new CPlayerBehaviour(*_Player);

        //we set the initial position
        m_iInitX = m_PlayerEntity->GetX();
        m_iInitY = m_PlayerEntity->GetY();

        //we set the height and the width of the entity
        m_PlayerEntity->SetHeight(m_vEntitySprites[m_iState].GetHeight());
        m_PlayerEntity->SetWidth(m_vEntitySprites[m_iState].GetWidth());
    }
}

//**************************
//Description : Render the Shot
//Parameters : None
//Return Value : None
//Note : None
//**************************
void CRenderPlayer::RenderShots()
{
    //width and height and position of a shot animation
    int wSh, hSh, xSh, ySh;
    //sprite of a shot
    CSprite * SpriteShot = NULL;

    //we get the list of shots of the entity
    std::list<CDefaultShotBehaviour*> * ListShots = m_PlayerEntity->HasShots();

    //we erase all shots out of the screen
    m_PlayerEntity->PopShotOutOfScreen();

    SDL_Rect AreaDisplayed, Dest;

    //for all the shots
    for (std::list<CDefaultShotBehaviour *>::iterator it = ListShots->begin();
        it != ListShots->end();
        it++)
    {
        //we get the sprite
        SpriteShot = (*it)->GetSprite();

        //we get the height and width
        wSh = SpriteShot->GetWidth();
        hSh = SpriteShot->GetHeight();
        xSh = (*it)->GetX();
        ySh = (*it)->GetY();

        //we update the position
        (*it)->EntityTrajectory();

        //we draw it partially if it isn't completely in the screen
        if ( !(*it)->IsInTheScreen() )
        {
            Dest.x = ((xSh - wSh/2) > GAME_WINDOW_X) ? (xSh-wSh/2) : GAME_WINDOW_X;
            Dest.y = ((ySh - hSh/2) > 0) ? (ySh-hSh/2) : 0;

            AreaDisplayed.x = Dest.x - (xSh - wSh/2);
            AreaDisplayed.y = Dest.y - (ySh - hSh/2);
            AreaDisplayed.w = ( (xSh + wSh/2) >= (GAME_WINDOW_X + GAME_WINDOW_WIDTH) ) ?
                                (GAME_WINDOW_X + GAME_WINDOW_WIDTH - Dest.x) : ((xSh + wSh/2) - Dest.x);
            AreaDisplayed.h = ( (ySh + hSh/2) >= (GAME_WINDOW_HEIGHT) ) ?
                                (GAME_WINDOW_HEIGHT - Dest.y) : ((ySh + hSh/2) - Dest.y);

            //we set the position of the sprite
            SpriteShot->Set(xSh-wSh/2, ySh-hSh/2);

            //we draw it
            SpriteShot->Draw(&Dest, &AreaDisplayed);
        }
        else
        {
            //we set the position of the sprite
            SpriteShot->Set(xSh-wSh/2, ySh-hSh/2);

            //we draw it
            SpriteShot->Draw();
        }
    }
}

//**************************
//Description : Render the Entity
//Parameters : None
//Return Value : None
//Note : None
//**************************
void CRenderPlayer::RenderEntity()
{
    //if the player is not dead
    if ( m_iState != DEAD )
    {
        //we update the position
        m_PlayerEntity->EntityTrajectory();

        //The player shoot
        m_PlayerEntity->Shoot(m_vShotsAnimations, m_Screen);

        //we get the height and the width of the current animation
        int wSp = m_vEntitySprites[m_iState].GetWidth();
        int hSp = m_vEntitySprites[m_iState].GetHeight();


        //we set the position of the player
        m_vEntitySprites[m_iState].Set(m_PlayerEntity->GetX()-wSp/2, m_PlayerEntity->GetY()-hSp/2);

        //we draw it
        m_vEntitySprites[m_iState].Draw();

        if ( (m_iState == BLINK) && ( (Uint32)(m_lBeginBlink + BLINK_PLAYER_TIME) < SDL_GetTicks() ) )
        {
            m_iState = STANDBY;
        }
    }
    else
    {
        //we draw one the animation of the death
        //the animation of the dead is not finished, we continue it
        if ( !m_vEntitySprites[m_iState].EndAnimation() )
        {
            //we don't have to set the trajectory because when the enemy die, it don't move anymore
            m_vEntitySprites[m_iState].Draw();
        }
        else
        {
            //if the player is not dead
            if ( !m_PlayerEntity->IsDead() )
            {
                //it can be drawn one more time
                m_vEntitySprites[m_iState].RewindAnim();

                //the state is now BLINK
                m_iState = BLINK;

                //we save the beginning of the blinking
                m_lBeginBlink = SDL_GetTicks();

                //we set the initial position of the player
                m_PlayerEntity->SetPosition(m_iInitX, m_iInitY);
            }
        }
    }
}

//**************************
//Description : Render the entity and all things that have connections with it (shots of an entity, power up after death of an enemy, ...)
//Parameters : None
//Return Value : None
//Note : None
//**************************
void CRenderPlayer::Render()
{
    //we render if the screen is not null
    if (m_Screen != NULL)
    {
        //we render the entity
        RenderEntity();

        //we render shots
        RenderShots();
    }
}

//**************************
//Description : Test if the player collide with the ennemy and/or its shots
//Parameters : None
//Return Value : True if there is collision, false if not
//Note : None
//**************************
bool CRenderPlayer::CollideWithEnemy(CRenderEnemy * _Enemy)
{
    //bCollision is for a collision with an enemy or a shot, not a powerup
    bool bCollision = false;

    CDefaultEnemyBehaviour * _EnemyBehaviour = _Enemy->GetEnemyBehaviour();
    bool bEnemyDead = _EnemyBehaviour->IsDead();

    //We can collide if we aren't dead
    if ( m_iState != DEAD  )
    {
        //if we are not resurecting, we can collide with enemy and its shots
        if  ( m_iState != BLINK )
        {
            //we can collide with the enemy if it's not dead
            if ( !bEnemyDead )
                //We test the collision with the enemy
                bCollision = m_PlayerEntity->CollideWith( _EnemyBehaviour );

            std::list<CDefaultShotBehaviour *> * ListEnemyShots = _EnemyBehaviour->HasShots();

            std::list<CDefaultShotBehaviour *>::iterator it = ListEnemyShots->begin();

            //we test collision with enemy shots, until we find a shot that hit us because one shot can kill us
            while (it != ListEnemyShots->end() && !bCollision)
            {
                //we test the collision
                bCollision = (*it)->CollideWith( m_PlayerEntity );

                //if there is collision
                if (bCollision)
                    //we delete the shot
                    it = _EnemyBehaviour->PopShot(it);
                else
                    //else we go the the next shot
                    ++it;
            }

            if (bCollision)
            {
                //we rewind the STANDBY animation
                m_vEntitySprites[m_iState].RewindAnim();

                //if collision, the player is DEAD
                m_iState = DEAD;

                int wSp = m_vEntitySprites[m_iState].GetWidth();
                int hSp = m_vEntitySprites[m_iState].GetHeight();

                //we set the position of the sprite here, because it don't move anymore
                m_vEntitySprites[m_iState].Set(m_PlayerEntity->GetX()-wSp/2, m_PlayerEntity->GetY()-hSp/2);

                //the player lose a life and lose points
                m_PlayerEntity->LoseLife();
                m_PlayerEntity->AddPoints(-2000);
            }
        }

        //if the enemy is dead, we can collide with its power up if any
        if ( bEnemyDead )
        {
            CDefaultPowerUpBehaviour * PowerUp = _Enemy->GetPowerUpBehaviour();

            if ( _Enemy->HasPowerUpBehaviour() && m_PlayerEntity->CollideWith(PowerUp) )
            {
                PowerUp->GiveBonus(m_PlayerEntity);
                _Enemy->LosePowerUpBehaviour();
            }
        }
    }

    return bCollision;
}

//**************************
//Description : Test if the player is resurecting or not
//Parameters : None
//Return Value : True if the player is blinking (when resurection), false if not
//Note : It allows us to don't test collision when the player don't blink
//**************************
bool CRenderPlayer::IsResurecting() const
{
    return ( m_iState == BLINK );
}

//**************************
//Description : Test if the player is resurecting or not
//Parameters : None
//Return Value : True if the player is eploding, false if not
//Note : None
//**************************
bool CRenderPlayer::IsExploding() const
{
    return ( m_iState == DEAD && !m_vEntitySprites[m_iState].EndAnimation() );
}

//**************************
//Description : Test if the player is dead
//Parameters : None
//Return Value : True if the player is considered as dead, otherwise false
//Note : The player is considered has dead when its animation of death has finished
//**************************
bool CRenderPlayer::IsPlayerDead() const
{
    //if the player is dead and its animation of death has ended
    //if the player is dead, the state is compulsory DEAD, so we test the right animation
    return ( m_PlayerEntity->IsDead() && m_vEntitySprites[m_iState].EndAnimation() );
}

//**************************
//Description : Test if the player is touched by an enemy or a shot
//Parameters : None
//Return Value : True if the player is exploded (end of the animation of death), otherwise false
//Note : The player is considered as exploded when its animation of death has finished
//      It doesn't means that the player is TOTALLY dead
//**************************
bool CRenderPlayer::IsPlayerExplosed() const
{
    return ( m_iState == DEAD && m_vEntitySprites[m_iState].EndAnimation() );
}
